package cli

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"log"
	"net"
	"os"
	"path/filepath"
	"strings"

	D "gitee.com/dark.H/go-remote-repl/datas"
	"gitee.com/dark.H/go-remote-repl/lpc"
	"gitee.com/dark.H/go-remote-repl/remote"
	"github.com/Qingluan/Tunnel/tcp"
	"github.com/google/uuid"
)

func ArrayExists(arrays []string, one string) (newargs []string, out bool) {

	for _, a := range arrays {
		if a == one {
			out = true
		} else {
			newargs = append(newargs, a)
		}
	}
	return
}

func HandleJsonToAPIDB(data D.Gi) {
	apiinfo := new(lpc.APIInfo)
	apiinfo.Flags = make(map[string]lpc.Flag)

	raw_name := data["path"].(string)
	apiinfo.Name = data["name"].(string)
	space := uuid.UUID{}
	h := uuid.NewHash(md5.New(), space, []byte(raw_name), 1)
	binpath := filepath.Join(os.TempDir(), h.String())
	binpath += "rpcapi"
	if strings.Contains(filepath.Base(raw_name), ".") {
		ens := strings.Split(filepath.Base(raw_name), ".")
		binpath += "." + ens[len(ens)-1]
	}
	apiinfo.BinFilePath = binpath
	if executable, ok := data["executable"]; ok {
		apiinfo.Executale = executable.(string)
	}
	for n, v := range data["options"].(map[string]interface{}) {
		apiinfo.Flags[n] = lpc.Flag{
			Name:     n,
			Usage:    v.(string),
			DefValue: "",
			Value:    "",
		}
	}
	lpc.APIDBUpdate(apiinfo)
}

func SyncAPI(apiinfo *lpc.APIInfo, conn net.Conn, passFile ...bool) (res bool) {

	defer conn.Close()
	data, err := json.Marshal(apiinfo)
	if err != nil {
		D.JsonErr(err, conn)
		return
	}
	payload := D.Data{
		"Op":   "update",
		"data": data,
	}
	if passFile != nil {
		payload["file"] = apiinfo.BinFilePath
		defer func() {

		}()
	}
	if D.JsonSend(payload, conn) > 0 {
		if _, ok := payload["file"]; ok {
			log.Println("wait ..")
			f, err := os.Open(apiinfo.BinFilePath)
			if err != nil {
				log.Println("sync err:", err)
			}
			defer f.Close()
			tcp.PipeFile(conn, f)
		}
		if reply := D.JsonRecv(conn); reply != nil {
			if v, ok := reply["Code"]; ok && v.(bool) {
				res = true
			}

			fmt.Println(reply)
		} else {
		}
	}
	return
}

func ClientConnect(service string, configs ...interface{}) {
	var con net.Conn
	var err error
	if configs == nil {
		configs = append(configs, "tls")
	}
	con, _, err = tcp.ExpressDial(service, configs...)
	if err != nil {
		log.Fatal(err)
	}
	man := remote.NewApiConn(con, "menu")
	tcp.Pipe(con, man)
}

func ClientSync(service, file string, configs ...interface{}) string {
	var con net.Conn
	var err error
	if configs == nil {
		configs = append(configs, "tls")
	}
	con, _, err = tcp.ExpressDial(service, configs...)

	if err != nil {
		log.Fatal(err)
	}
	man := remote.NewApiConn(con, "File")
	return man.SendFile(file)
	// D.Pipe(con, man)
}

func ClientDB(service string, configs ...interface{}) string {
	var con net.Conn
	var err error
	if configs == nil {
		configs = append(configs, "tls")
	}
	con, _, err = tcp.ExpressDial(service, configs...)

	if err != nil {
		log.Fatal(err)
	}
	man := remote.NewApiConn(con, "data")
	tcp.Pipe(con, man)
	return ""
}

func ClientSyncAPIFileByJSON(service, data string, configs ...interface{}) (res bool) {
	var con net.Conn
	var err error
	if configs == nil {
		configs = append(configs, "tls")
	}
	con, _, err = tcp.ExpressDial(service, configs...)

	if err != nil {
		log.Fatal(err)
	}
	cli := remote.NewApiConn(con, "File")
	dataJ := make(D.Gi)
	json.Unmarshal([]byte(data), &dataJ)
	if _, err := os.Stat(dataJ["path"].(string)); err == nil {
		cli.SendFile(dataJ["path"].(string), dataJ)
		return true
	}
	return false
}

func ClientLPC(name string, getCmdAndConImplementFunc func(label string, options D.G) (string, net.Conn, error)) {
	if api, ok := lpc.APIDB[name]; ok {
		options := make(D.G)
		for n, v := range api.Flags {
			if strings.HasPrefix(n, "-") {
				options[n] = v.Usage
			} else {
				options["-"+n] = v.Usage
			}
		}
		if cmd, srcCon, err := getCmdAndConImplementFunc("rpc call > ", options); err != nil {
			log.Println("get cmd input: err:", err)
			return
		} else {
			if uco := lpc.SimpleClient(lpc.SOCK_FILE_PATH); uco != nil {
				D.JsonSend(D.Data{
					"Op":   "do",
					"args": cmd,
					"name": name,
				}, uco)
				tcp.PipeFile(uco, srcCon)
			}
		}
	}
}
